/* Kanye@Home Server
 */

#include "Server.h"

Server::Server()
{
    WSADATA wsadata;
    struct addrinfo hints, *addrinfo = NULL, *AI;
    
    numsocks = 0;
    memset(&sockset, 0, sizeof(fd_set));

    if(WSAStartup(MAKEWORD(2,2), &wsadata))
    {
        throw "WSAStartupError";
    }

    sock = INVALID_SOCKET;
    int i;
    for(i = 0; i < FD_SETSIZE; i++) seversocks[i] = INVALID_SOCKET;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;

    if(getaddrinfo(NULL, "52693", &hints, &addrinfo))
    {
        throw std::string("GetAddrInfoError");
    }

    i = 0;
    for(AI = addrinfo; AI != NULL; AI = AI->ai_next)
    {
        if (i == FD_SETSIZE) break;

        if((AI->ai_family == PF_INET) || (AI->ai_family == PF_INET6))
        {
            seversocks[i] = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
            if (seversocks[i] != INVALID_SOCKET)
            {
                if (bind(seversocks[i], AI->ai_addr, AI->ai_addrlen) == SOCKET_ERROR) closesocket(seversocks[i]);
                else
                {
                    if (listen(seversocks[i], 5) == SOCKET_ERROR)
                    {
                        closesocket(seversocks[i]);
                        continue;
                    }
                    i++;
                }
            }
        }
    }
    numsocks = i;

    if(numsocks == 0)
    {
        throw std::string("NoSocketsError");
    }

    freeaddrinfo(addrinfo);
}

Server::~Server()
{
    Cleanup();
}

void Server::Listen()
{
    SOCKADDR_STORAGE remoteaddress;
    int remoteaddresssize, bytesrecv, bytestotal;
    char buffer[256],clientaddress[128];

    FD_ZERO(&sockset);
    for (int i = 0; i < numsocks; i++) FD_SET(seversocks[i], &sockset);

    if (select(numsocks, &sockset, 0, 0, NULL) == SOCKET_ERROR)
    {
        throw std::string("SelectError");
    }

    for (int i = 0; i < numsocks; i++)
    {
        if (FD_ISSET(seversocks[i], &sockset))
        {
            FD_CLR(seversocks[i], &sockset);
            remoteaddresssize = sizeof(remoteaddress);
            sock = accept(seversocks[i], (SOCKADDR*)&remoteaddress, &remoteaddresssize);
            if(sock == INVALID_SOCKET)
            {
                throw std::string("InvalidSocket");
            }
            break;        // Only need one socket
        }
    }
    bytestotal = 0;

    do
    {
        remoteaddresssize = sizeof(remoteaddress);
        bytesrecv = recvfrom(sock, buffer + bytestotal, sizeof(buffer) - bytestotal, 0, (SOCKADDR *)&remoteaddress, &remoteaddresssize);
        bytestotal += bytesrecv;
    } while(bytesrecv > 0 && bytestotal < sizeof(buffer));

    if(bytesrecv == SOCKET_ERROR)
    {
        throw std::string("SocketError");
    }
    else if(bytesrecv == 0)
    {
        throw std::string("NoData");
    }

    remoteaddresssize = sizeof(remoteaddress);
    getpeername(sock, (SOCKADDR *)&remoteaddress, &remoteaddresssize);

    if (getnameinfo((SOCKADDR *)&remoteaddress, remoteaddresssize, clientaddress, sizeof(clientaddress), NULL, 0, NI_NUMERICHOST) != 0)strcpy(clientaddress, "");

    bytesrecv = 0;
    bytesrecv = sendto(sock, buffer, bytestotal, 0, (SOCKADDR *)&remoteaddress, remoteaddresssize);
}

void Server::Cleanup()
{
    for(int i = 0; i < numsocks && seversocks[i] != INVALID_SOCKET; i++) closesocket(seversocks[i]);

    if(sock != INVALID_SOCKET)
    {
        shutdown(sock, SD_BOTH);
        closesocket(sock);
    }

    WSACleanup();

    return;
}